#pragma once

#include "buffer.hpp"
#include <mutex>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <functional>
namespace yd
{
    class async_looper
    {
        public:
        using functor = std::function<void(buffer &buf)>;
        using ptr = std::shared_ptr<async_looper>;
        async_looper(const functor &cb)
            : _looper_callback(cb), _running(true), _thread(std::thread(&async_looper::worker_loop, this))
        {
        }
        ~async_looper()
        {
            stop();
        }
        void stop()
        {
            _running = false;
            _pop_cond.notify_all();
            _thread.join();
        }
        void push(const std::string &msg)
        {
            if (_running == false)
                return;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _push_cond.wait(lock, [&]()
                                { return _tasks_push.write_ablesize() >= msg.size(); });
                _tasks_push.push(msg.c_str(), msg.size());
            }
            _pop_cond.notify_all();
        }

    private:
        functor _looper_callback;
        std::mutex _mutex;
        buffer _tasks_push;
        buffer _tasks_pop;
        std::thread _thread;
        std::condition_variable _push_cond;
        std::condition_variable _pop_cond;
        std::atomic<bool> _running;

    private:
        void worker_loop()
        {
            while (1)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    if (_running == false && _tasks_push.empty())
                    {
                        return;
                    }
                    _pop_cond.wait(lock, [&]
                                   { return !_tasks_push.empty() || !_running; });
                    _tasks_push.swap(_tasks_pop);
                }
                _push_cond.notify_all();
                _looper_callback(_tasks_pop);
                _tasks_pop.reset();
            }
            return ;
        }
    };
}